package eight;

import java.util.*;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.stream.Collectors;

public class SqlManualJoin {
    public static final class JoinAble{

        //注册一个joinAble
        private Consumer<JoinAble> doJoin=null;

        private List<JoinAble> child_list;

        //用户在连接时可能的回调
        private Consumer<Map<String,Object>> self_call;

        //用于child获取ids放在in条件中
        private List<Map<String,Object>> res;

        private JoinAble(){

        }

        //不想暴露内部JoinAble,所以把相关上下文信息聚合到一起.
        public interface Context {
            default String getSql(String sql){
                return "";
            }
            default Set<Object> getParentIds(){
                return new HashSet<>();
            }

            //每一行被连接过后的回调
            void left_row(Consumer<Map<String,Object>> parent_row);
        }

        public JoinAble leftJoin(String on_ParentColumn, String on_ChildColumn, Function<Context,List<Map<String,Object>>> joinAble){
            Consumer<JoinAble> child_doJoin=self->{
                List<Map<String, Object>> data = getRes();
                Context context = new Context() {
                    //todo 还没用到
                    @Override
                    public String getSql(String sql) {
                        return on_ChildColumn + " as " + on_ParentColumn;
                    }

                    @Override
                    public Set<Object> getParentIds() {
                        return data.stream().map(i -> i.get(on_ParentColumn)).filter(Objects::nonNull).collect(Collectors.toSet());
                    }

                    @Override
                    public void left_row(Consumer<Map<String, Object>> d) {
                        self.setSelf_call(d);
                    }
                };
                List<Map<String, Object>> joinData = joinAble.apply(context);

                if (joinData==null){
                    joinData=new ArrayList<>(0);
                }

                self.setRes(joinData);
                final Map<Object, Map<String, Object>> collect = joinData.stream().collect(Collectors.toMap(i -> i.get(on_ChildColumn), Function.identity()));
                //??这里怎么会这样?我们不是已知是否有self_call了吗?
                if(self.getSelf_call()!=null){
                    Consumer<Map<String, Object>> customer = self.getSelf_call();
                    self.setSelf_call(row->{
                        Map<String, Object> x = collect.get(row.get(on_ParentColumn));
                        if (x != null) {
                            row.putAll(x);
                            customer.accept(row);
                        }
                    });
                }else{
                    self.setSelf_call(row->{
                        Map<String, Object> x = collect.get(row.get(on_ParentColumn));
                        if (x != null) {
                            row.putAll(x);
                        }
                    });
                }

            };
            JoinAble child = new JoinAble();
            child.setDoJoin(child_doJoin);
            child.setChild_list(new ArrayList<>());
            assert child_list != null;
            child_list.add(child);
            return child;
        }

        //两者相同的公共列进行join
        public JoinAble leftJoin(String common_column,Function<Context,List<Map<String,Object>>> joinAble){
            return leftJoin(common_column,common_column,joinAble);
        }

        private static final class Ix{
            public JoinAble self;

            public JoinAble getSelf() {
                return self;
            }

            public void setSelf(JoinAble self) {
                this.self = self;
            }
        }

        //注册的连接,可以获取自身作为参数,继续注册子连接,达到懒注册
        public JoinAble leftJoin(String on_ParentColumn, String on_ChildColumn, BiFunction<Context,JoinAble,List<Map<String,Object>>> joinAble) {
            //并不优雅的方式
            Ix t= new Ix();
            Function<Context,List<Map<String,Object>>> wrapper_join_able=context->{
                return joinAble.apply(context,t.getSelf());
            };
            t.setSelf(leftJoin(on_ParentColumn,on_ChildColumn,wrapper_join_able));
            return t.getSelf();
        }

        public JoinAble leftJoin(String common_column,BiFunction<Context,JoinAble,List<Map<String,Object>>> joinAble){
            return leftJoin(common_column,common_column,joinAble);
        }

        public JoinAble leftJoin_n(Consumer<JoinAble> composite){
            composite.accept(this);
            return this;
        }

        private Consumer<Map<String, Object>> getSelf_call() {
            return self_call;
        }

        private void setSelf_call(Consumer<Map<String, Object>> self_call) {
            this.self_call = self_call;
        }

        private List<Map<String, Object>> getRes() {
            return res;
        }

        private void setRes(List<Map<String, Object>> res) {
            this.res = res;
        }

        private List<Map<String,Object>> getData(List<Object> id_column){
            return getRes();
        }

        private void setData(List<Map<String,Object>> data){
            setRes(data);
        }

        private List<JoinAble> getChild_list() {
            return child_list;
        }

        private void setChild_list(List<JoinAble> child_list) {
            this.child_list = child_list;
        }

        private Consumer<JoinAble> getDoJoin() {
            return doJoin;
        }

        private void setDoJoin(Consumer<JoinAble> doJoin) {
            this.doJoin = doJoin;
        }

        public static Jexecute MakeJoinAble(Function<JoinAble,List<Map<String,Object>>> dataSource){

            JoinAble root = new JoinAble();
            root.setDoJoin(self->{
                self.setRes(dataSource.apply(self));
            });
            root.setSelf_call(i->{
                //在每次合并一行后进行回调
//                System.out.println("root default self_call");
            });
            root.setChild_list(new ArrayList<>());
            Jexecute jexecute = new Jexecute();
            jexecute.setRoot(root);
            return jexecute;
        }

        public static Jexecute MakeJoinAble(BiFunction<Context, JoinAble,List<Map<String,Object>>> dataSource){

            JoinAble root = new JoinAble();
            root.setDoJoin(self->{
                //todo 从这里开始
                self.setRes(dataSource.apply(self::setSelf_call,self));
            });
            if(root.getSelf_call()==null){
                root.setSelf_call(i->{});
            }
            root.setChild_list(new ArrayList<>());
            Jexecute jexecute = new Jexecute();
            jexecute.setRoot(root);
            return jexecute;
        }

        private static List<Map<String,Object>> execute(JoinAble root){
            Queue<JoinAble> queue=new LinkedList<>();
            Queue<JoinAble> queue2=new LinkedList<>();
            queue.add(root);
            JoinAble item=null;
            while (!queue.isEmpty()){
                item=queue.peek();
                item.getDoJoin().accept(item);
                if(!item.getRes().isEmpty()) {
                    queue.addAll(item.getChild_list());
                    queue2.add(item);//只有自身有数据才参与之后的join
                }
                queue.remove();
            }
            //下面连接
            //移除root
            if(!queue2.isEmpty()){
                queue2.remove();
            }
            if(queue2.isEmpty()){
                return root.getRes();
            }
            List<JoinAble> joinAbles = queue2.stream().collect(Collectors.toList());
            Consumer<Map<String, Object>> root_self_call = root.getSelf_call();
            BiConsumer<Map<String,Object>,List<JoinAble>> mx=null;
            if(joinAbles.size()==1){
                mx=JoinAble::m1;
            }else if(joinAbles.size()==2){
                mx=JoinAble::m2;
            }else if(joinAbles.size()==3){
                mx=JoinAble::m3;
            } else if(joinAbles.size()==4){
                mx=JoinAble::m4;
            }else{
                mx=(item2,list)->{
                    for(JoinAble joina:list){
                        joina.getSelf_call().accept(item2);
                    }
                };
            }
            for(Map<String, Object> itemx:root.getRes()){
                mx.accept(itemx,joinAbles);
                root_self_call.accept(itemx);
            }
            return root.getRes();
        }

        private static void m1(Map<String,Object> item,List<JoinAble> list){
            assert list.size()==1;
            list.get(0).getSelf_call().accept(item);
        }
        private static void m2(Map<String,Object> item,List<JoinAble> list){
            assert list.size()==2;
            list.get(0).getSelf_call().accept(item);
            list.get(1).getSelf_call().accept(item);
        }
        private static void m3(Map<String,Object> item,List<JoinAble> list){
            assert list.size()==3;
            list.get(0).getSelf_call().accept(item);
            list.get(1).getSelf_call().accept(item);
            list.get(2).getSelf_call().accept(item);
        }
        private static void m4(Map<String,Object> item,List<JoinAble> list){
            assert list.size()==4;
            list.get(0).getSelf_call().accept(item);
            list.get(1).getSelf_call().accept(item);
            list.get(2).getSelf_call().accept(item);
            list.get(3).getSelf_call().accept(item);
        }

        public static final class Jexecute{
            JoinAble root;

            public JoinAble getRoot() {
                return root;
            }

            public void setRoot(JoinAble root) {
                this.root = root;
            }

            public List<Map<String, Object>> execute(){
                return JoinAble.execute(root);
            }
        }

    }
}
